package com.onpositive.realmview.queryeditor;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.commands.operations.ObjectUndoContext;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ColumnPixelData;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.EditorPart;

import com.onpositive.queryeditor.util.SelectionUtils;
import com.onpositive.realmview.AbstractRealmView;
import com.onpositive.realmview.IRealmView;
import com.onpositive.realmview.model.ColumnConfiguration;
import com.onpositive.realmview.model.IHasViews;
import com.onpositive.realmview.model.ILabel;
import com.onpositive.realmview.model.IViewGrouping;
import com.onpositive.realmview.model.filtering.IBooleanFilter;
import com.onpositive.realmview.model.filtering.IUIFilter;
import com.onpositive.realmview.search.GlobalSearch;
import com.onpositive.semantic.common.ui.roles.ImageManager;
import com.onpositive.semantic.common.ui.roles.ImageObject;
import com.onpositive.semantic.model.api.property.IProperty;
import com.onpositive.semantic.model.api.property.IPropertyProvider;
import com.onpositive.semantic.model.api.undo.IExecutableOperationUndoable;
import com.onpositive.semantic.model.api.undo.support.UndoRedoSupport;
import com.onpositive.semantic.model.realm.FilteringRealm;
import com.onpositive.semantic.model.realm.IModifiableRealm;
import com.onpositive.semantic.model.realm.IRealm;
import com.onpositive.semantic.model.realm.IRealmChangeListener;
import com.onpositive.semantic.model.realm.ISetDelta;
import com.onpositive.semantic.model.realm.ISimpleChangeListener;
import com.onpositive.semantic.model.ui.property.editors.structured.columns.Column;

public abstract class QueryEditorPart extends EditorPart {

	public static interface IRealmViewChangeListener {
		void realmChanged();
	}

	private List<IRealmViewChangeListener> lists;
	protected Map<IBooleanFilter, Action> qfaction = new HashMap<IBooleanFilter, Action>();

//	protected void refreshDirty() {
//		IBooleanFilter f = null;
//		Action a = null;
//		for (IBooleanFilter ff : qfaction.keySet()) {
//			if (ff instanceof NotCompletedFilter) {
//				f = ff;
//				a = qfaction.get(ff);
//				break;
//			}
//		}
//		if (a != null && f != null) {
//			if (!a.isChecked()) {
//				copy.usedQuickFilters().add(f);
//				// QueryEditorPart.this.firePropertyChange(PROP_DIRTY);
//				copy.usedQuickFilters().remove(f);
//				// QueryEditorPart.this.firePropertyChange(PROP_DIRTY);
//			} else {
//				copy.usedQuickFilters().remove(f);
//				// QueryEditorPart.this.firePropertyChange(PROP_DIRTY);
//				copy.usedQuickFilters().add(f);
//				// QueryEditorPart.this.firePropertyChange(PROP_DIRTY);
//			}
//		}
//
//	}

	public void addIRealmViewChangeListener(IRealmViewChangeListener l) {
		if (lists == null) {
			lists = new ArrayList<IRealmViewChangeListener>();
		}

		lists.add(l);
	}

	public void removeIRealmViewChangeListener(IRealmViewChangeListener l) {
		if (lists != null) {
			lists.remove(l);
		}
	}

	protected void fireIRealmChangeD() {
		if (lists != null) {
			for (IRealmViewChangeListener l : lists) {
				l.realmChanged();
			}
		}
	}

	protected IRealmView copy;
	protected ArrayList<GroupByAction> groupBy = new ArrayList<GroupByAction>();
	protected ArrayList<IAction> filterAction = new ArrayList<IAction>();
	protected ArrayList<IAction> optionsAction = new ArrayList<IAction>();
	protected Action filtersAction;

	protected IPropertyProvider provider;
	boolean sortInverse = false;
	protected IRealmView query;
	private FilteringRealm<Object> currentRealm;

	public void setCopy(IRealmView view) {
		this.copy = view;
		initSort(copy);
		currentRealm = (FilteringRealm<Object>) copy.getRealm();
		currentRealm
				.addRealmChangeListener(new IRealmChangeListener<Object>() {

					public void realmChanged(IRealm<Object> realmn,
							ISetDelta<Object> delta) {
						if (!delta.isEmpty()) {
							editorRealmChanged();
						} else {
							Display.getDefault().asyncExec(new Runnable() {

								public void run() {

									firePropertyChange(PROP_DIRTY);

								}
							});
						}
					}

				});
		internalInit(view);
	}

	final class GroupByAction extends Action {
		final IViewGrouping p;

		private GroupByAction(String text, int style, IViewGrouping p) {
			super(text, style);

			this.p = p;
		}

		public void run() {
			setupGrouping(p);
		}
	}

	public void setFocus() {

	}

	public void setupGrouping(IViewGrouping p) {

	}

	public void setupSecondGrouping(IViewGrouping p) {

	}

	public void doSaveAs() {
		// TODO Auto-generated method stub

	}

	protected final class QueryViewerSoerter extends ViewerSorter {

		public int compare(Viewer viewer, Object e1, Object e2) {
			if (e1 instanceof Object && e2 instanceof Object) {
				Object r1 = (Object) e1;
				Object r2 = (Object) e2;
				Object propertyValue = getPropValue(r1);
				Object propertyValue1 = getPropValue(r2);
				if (propertyValue instanceof Comparable
						&& propertyValue1 instanceof Comparable) {
					Comparable p = (Comparable) propertyValue;
					Comparable p1 = (Comparable) propertyValue1;
					if (p instanceof String || p1 instanceof String) {
						System.out.println("A");
					}
					try {
						int compareTo = p.compareTo(p1);
						if (sortInverse) {
							return -compareTo;
						}
						return compareTo;
					} catch (Exception e) {

					}
				}
				int compare = super.compare(viewer, propertyValue,
						propertyValue1);
				if (sortInverse) {
					return -compare;
				}
				return compare;
			}
			if (e1 instanceof GroupNode && e2 instanceof GroupNode) {
				GroupNode r1 = (GroupNode) e1;
				GroupNode r2 = (GroupNode) e2;

				Object propertyValue = r1.getFieldValue(currentSorting);
				if (propertyValue == null) {
					propertyValue = "";
				}
				Object propertyValue1 = r2.getFieldValue(currentSorting);
				if (propertyValue1 == null) {
					propertyValue1 = "";
				}
				if (propertyValue instanceof Comparable
						&& propertyValue1 instanceof Comparable) {
					Comparable p = (Comparable) propertyValue;
					Comparable p1 = (Comparable) propertyValue1;
					try {
						int compareTo = p.compareTo(p1);
						if (sortInverse) {
							return -compareTo;
						}
						return compareTo;
					} catch (Exception e) {

					}
				}
			}
			return super.compare(viewer, e1, e2);
		}

		private Object getPropValue(Object r1) {
			if (currentSorting == null) {
				return getTitle(r1);
			}
			IProperty property = provider.getProperty(r1, currentSorting);
			Object propertyValue = property == null ? null : property
					.getValue(r1);
			if (propertyValue == null) {
				propertyValue = "";
			}
			return propertyValue;
		}
	}

	protected void initActionStates(){
		
	}
	
	protected Object getTitle(Object r1) {
		return r1.toString();
	}

	public void init(IEditorSite site, IEditorInput input)
			throws PartInitException {
		super.setInput(input);
		if (site != null) {
			super.setSite(site);
		}
		query = ((QueryInput) input).getView();

		boolean enabled = false;
		IHasViews<?> p = query.getParent();
		
		if (!enabled) {
			((AbstractRealmView) query).setSyncViewWithPrivateServer(enabled);
			query.getSettings().put(AbstractRealmView.SYNC_WITH_PRIVATE_SERVER_MODE_ID, "false");
			initActionStates();
		}

		query.getSettings().put(AbstractRealmView.SYNC_WITH_PRIVATE_SERVER_MODE_ID, "false"); //TODO why do we need this

		copy = query.clone();
		internalInit(query);
	}

	public abstract class FilterOperationUndoable implements
			IExecutableOperationUndoable {

		public abstract Object execute();

		public abstract Object undo();

		public Object redo() {
			return execute();
		}

		public Object getUndoContext() {
			return new ObjectUndoContext(QueryEditorPart.this);
		}

		public Object getTarget() {
			return null;
		}

	}

	protected void internalInit(final IRealmView query) {
		justUpdateStates(query);

		filtersAction = new Action("Configure Filters", Action.AS_PUSH_BUTTON) {
			{
				setImageDescriptor(ImageManager.getInstance()
						.get("com.onpositive.wiki.filters.configure")
						.getImageDescriptor());
			}

			public void run() {
				configureFilters(query, 1);
			}
		};

		IViewGrouping last = null;
		Collection<IViewGrouping> avalableClusterizationPointProviders = query
				.getParent().getQueryProvider().getGroupings();
		for (final IViewGrouping p : avalableClusterizationPointProviders) {

			final GroupByAction createImageHyperlink = new GroupByAction(
					p.getLabel(), Action.AS_RADIO_BUTTON, p);
			groupBy.add(createImageHyperlink);
			String imageID = p.getImageID();
			if (imageID != null && imageID.length() > 0) {
				createImageHyperlink.setImageDescriptor(ImageManager
						.getInstance().get(imageID).getImageDescriptor());
			}
			if (p.equals(copy.getCurrentGrouping())) {
				createImageHyperlink.setChecked(true);
			}
		}
		Collection<IBooleanFilter> quickFilters = query.getParent()
				.getQueryProvider().getQuickFilters();
		// ToolBarManager manager = new ToolBarManager();
		//
		for (final IBooleanFilter p : quickFilters) {
			// layout.numColumns++;
			Action action = new Action(p.getLabel(), Action.AS_CHECK_BOX) {
				public void run() {
					if (isChecked()) {
						copy.usedQuickFilters().add(p);

					} else {
						copy.usedQuickFilters().remove(p);
					}
					QueryEditorPart.this.firePropertyChange(PROP_DIRTY);
				}
			};
			qfaction.put(p, action);
			this.filterAction.add(action);
			IModifiableRealm<IBooleanFilter> used = query.usedQuickFilters();
			boolean contains = used.contains(p);
			action.setChecked(contains);

			ImageObject imageObject = ImageManager.getInstance().get(
					p.getImageId());
			if (imageObject != null) {
				action.setImageDescriptor(imageObject.getImageDescriptor());
			}
		}
	}

	/**
	 * @param query
	 */
	protected void justUpdateStates(final IRealmView query) {
		GlobalSearch.addListener(searchListener);
		initSort(query);
		IRealm<?> queryRealm = copy.getRealm();
		currentRealm = (FilteringRealm<Object>) queryRealm;
		IRealmChangeListener<Object> listener = new IRealmChangeListener<Object>() {

			public void realmChanged(IRealm<Object> realmn,
					ISetDelta<Object> delta) {
				if (!delta.isEmpty()) {
					editorRealmChanged();
				} else {
					Display.getDefault().asyncExec(new Runnable() {

						public void run() {

							firePropertyChange(PROP_DIRTY);

						}
					});
				}
			}

		};
		currentRealm.addRealmChangeListener(listener);

		copy.filters().addRealmChangeListener(
				new IRealmChangeListener<IUIFilter>() {

					public void realmChanged(IRealm<IUIFilter> realmn,
							ISetDelta<IUIFilter> delta) {
						updateStale();
					}
				});
		updateStale();
		boolean f1 = removeStaleFilters.isEnabled();
		if (!f1) {
			removeStaleFilters.setEnabled(!f1);
			removeStaleFilters.setEnabled(f1);
		}
	}

	private void initSort(final IRealmView query) {
		currentSorting = query.getSettings().get(IRealmView.SORT_PROPERTY);
		if (currentSorting != null && currentSorting.trim().length() == 0) {
			currentSorting = null;
		}
		String s = query.getSettings().get(IRealmView.SORT_ORDER);
		if (s != null) {
			sortInverse = Boolean.parseBoolean(s);
		}
		provider = copy.getParent().getQueryProvider().provider();
	}

	protected void editorRealmChanged() {

	}

	protected Column createColumn(int size, ColumnConfiguration c) {
		Column column4 = new Column(c.getName(), c.getId()) {

			public Object getElement(Object item) {
				if (item instanceof GroupNode) {
					GroupNode m = (GroupNode) item;
					return m.getFieldValue(id);
				}

				return super.getElement(item);
			}

			protected Object internalGet(Object item) {
				if (item == null) {
					return null;
				}
				IProperty property = provider.getProperty(item, id);

				if (property == null) {
					return super.internalGet(item);
				}
				Object value = property.getValue(item);
				if (value instanceof Collection) {
					Collection c = (Collection) value;
					if (c.size() == 1) {
						return c.iterator().next();
					}
				}

				return value;
			}

			public Viewer getViewer() {
				return QueryEditorPart.this.getViewer();
			}

		};
		int px = c.getExpectedChars() * size;
		column4.setLayoutData(new ColumnPixelData(px));

		column4.setResizable(false);
		if (c.isImageInTitle()) {
			column4.setImage(c.getImageId());
			column4.setCaption("");
		}
		column4.setMovable(true);
		return column4;
	}

	protected abstract Viewer getViewer();

	protected abstract void searchChanged();

	@SuppressWarnings("unchecked")
	private void configureFilters(final IRealmView query, final int size) {
		new SelectionUtils("Filters", "Configure visible filters") {

			public boolean isDisposed() {
				return getControl() == null || getControl().isDisposed();
			}

			protected Set<Object> getElementGroup(Object o) {
				IUIFilter c = (IUIFilter) o;
				return (Set) c.getGroup();
			}

			protected Collection<Object> getContents() {
				ArrayList<Object> m = new ArrayList<Object>();
				for (IUIFilter z : copy.filters()) {
					m.add(copy.getParent().getQueryProvider()
							.getFilter(z.getId()));
				}
				return (Collection<Object>) m;
			}

			@SuppressWarnings("rawtypes")
			protected Collection<Object> getAllowedElements() {
				return (Collection) copy.getParent().getQueryProvider()
						.getPossibleFilters();
			}

			protected void doAdd(final Object ca) {
				UndoRedoSupport.getUndoRedoChangeManager().execute(
						new FilterOperationUndoable() {

							private IUIFilter flt;

							public Object undo() {
								if (removeStaleFilters != null) {
									copy.filters().remove(flt);
								}
								return Status.OK_STATUS;
							}

							public Object execute() {
								IUIFilter m = (IUIFilter) ca;
								flt = m.clone();
								copy.filters().add(flt);
								return Status.OK_STATUS;
							}
						});

			}

			protected void doRemove(Object ma) {
				final IUIFilter m = (IUIFilter) ma;
				UndoRedoSupport.getUndoRedoChangeManager().execute(
						new FilterOperationUndoable() {

							private IUIFilter flt;

							public Object undo() {
								if (removeStaleFilters != null) {
									copy.filters().add(flt);
								}
								return Status.OK_STATUS;
							}

							public Object execute() {
								for (IUIFilter a : copy.filters()) {
									if (a.getId().equals(m.getId())) {
										flt = a;
										copy.filters().remove(a);
										break;
									}
								}
								return Status.OK_STATUS;
							}
						});

			}

			protected void onChange() {
				getControl().getParent().layout(true, true);
				firePropertyChange(PROP_DIRTY);
			}

		}.doConfig();
	}

	private void updateStale() {
		boolean hasStale = false;
		for (IUIFilter f : copy.filters()) {
			if (f.isStale()) {
				hasStale = true;
			}
		}
		removeStaleFilters.setEnabled(hasStale);
	}

	public void configureColumns() {
		new SelectionUtils("Columns", "Configure visible columns") {

			public boolean isDisposed() {
				return getControl() == null || getControl().isDisposed();
			}

			protected Set<Object> getElementGroup(Object o) {
				ColumnConfiguration c = (ColumnConfiguration) o;
				return (Set) c.getGroup();
			}

			protected Collection<Object> getContents() {
				return (Collection) copy.columns().getContents();
			}

			protected Collection<Object> getAllowedElements() {
				return (Collection) copy.getParent().getQueryProvider()
						.getAllowedColumns();
			}

			protected void doAdd(Object ca) {
				ColumnConfiguration c = (ColumnConfiguration) ca;
				Column column4 = createColumn(c.getExpectedChars(), c);
				addColumn(column4);
				copy.columns().add(c);
			}

			protected void doRemove(Object ma) {
				ColumnConfiguration m = (ColumnConfiguration) ma;
				for (Column m1 : new ArrayList<Column>(getColumns())) {
					if (m1.getId().equals(m.getId())) {
						removeColumn(m1);
					}
				}
				copy.columns().remove(m);
			}

			protected void onChange() {
				getControl().getParent().layout(true, true);
				firePropertyChange(PROP_DIRTY);
			}

		}.doConfig();
	}

	public void configureLabels() {
		new SelectionUtils("Labels", "Configure visible labels") {

			public boolean isDisposed() {
				return getControl() == null || getControl().isDisposed();
			}

			protected Set<Object> getElementGroup(Object o) {
				ILabel c = (ILabel) o;
				return (Set) c.getCategory();
			}

			protected Collection<Object> getContents() {
				return (Collection) copy.labels().getContents();
			}

			protected Collection<Object> getAllowedElements() {
				return (Collection) copy.getParent().getQueryProvider()
						.getKnownLabels();
			}

			protected void doAdd(Object ca) {
				ILabel l = (ILabel) ca;
				copy.labels().add(l);
				refreshViewer();
			}

			protected void doRemove(Object ma) {
				ILabel l = (ILabel) ma;
				refreshViewer();
				copy.labels().remove(l);
			}

			protected void onChange() {

				firePropertyChange(PROP_DIRTY);
			}

		}.doConfig();
	}

	public void refreshViewer() {

	}

	public void configureSummaries() {
		new SelectionUtils("Summaries", "Configure active summaries") {

			public boolean isDisposed() {
				return getControl() == null || getControl().isDisposed();
			}

			protected Set<Object> getElementGroup(Object o) {
				ILabel c = (ILabel) o;
				return (Set) c.getCategory();
			}

			protected Collection<Object> getContents() {
				return (Collection) copy.summaries().getContents();
			}

			protected Collection<Object> getAllowedElements() {
				return (Collection) copy.getParent().getQueryProvider()
						.getKnownSummaryLabels();
			}

			protected void doAdd(Object ca) {
				ILabel l = (ILabel) ca;

				copy.summaries().add(l);
				refreshViewer();
			}

			protected void doRemove(Object ma) {
				ILabel l = (ILabel) ma;
				copy.summaries().remove(l);
				refreshViewer();
			}

			protected void onChange() {
				firePropertyChange(PROP_DIRTY);
			}

		}.doConfig();
	}

	protected Control getControl() {
		return null;
	}

	protected void removeColumn(Column m1) {

	}

	protected Collection<? extends Column> getColumns() {
		return null;
	}

	protected void addColumn(Column column4) {

	}

	public void dispose() {
		currentRealm.dispose();
		GlobalSearch.removeListener(searchListener);
		super.dispose();
	}

	private ISimpleChangeListener<String> searchListener = new ISimpleChangeListener<String>() {

		public void changed(String provider, Object extraData) {
			searchChanged();
		}
	};

	protected IRealm<?> getCurrentRealm() {
		return currentRealm;
	}

	@SuppressWarnings("unchecked")
	public List<IAction> getGroupByActions() {
		return (List) groupBy;
	}

	public void checkActions() {
		if (qfaction != null) {
			IModifiableRealm<IBooleanFilter> qf = copy.usedQuickFilters();
			Collection<IBooleanFilter> quickFilters = query.getParent()
					.getQueryProvider().getQuickFilters();
			for (IBooleanFilter f : quickFilters) {
				Action a = qfaction.get(f);
				if (qf.contains(f)) {
					a.setChecked(true);
				} else {
					a.setChecked(false);
				}
			}
		}
	}

	@SuppressWarnings("unchecked")
	public List<IAction> getQuickFilterActions() {
		return (List) filterAction;
	}

	@SuppressWarnings("unchecked")
	public List<IAction> getOptionsActions() {
		return (List) optionsAction;
	}

	public IRealmView getWorkingQuery() {
		return copy;
	}

	public IAction getFiltersAction() {
		return filtersAction;
	}

	Action removeStaleFilters = new Action("Remove Filters",
			Action.AS_PUSH_BUTTON) {
		{
			setImageDescriptor(PlatformUI.getWorkbench().getSharedImages()
					.getImageDescriptor(ISharedImages.IMG_TOOL_DELETE));
			setDisabledImageDescriptor(PlatformUI.getWorkbench()
					.getSharedImages()
					.getImageDescriptor(ISharedImages.IMG_TOOL_DELETE_DISABLED));
		}

		public void run() {
			UndoRedoSupport.getUndoRedoChangeManager().execute(
					new FilterOperationUndoable() {

						private Set<IUIFilter> removeStaleFilters2;

						public Object undo() {
							if (removeStaleFilters != null) {
								copy.filters().add(removeStaleFilters2);
							}
							return Status.OK_STATUS;
						}

						public Object execute() {
							removeStaleFilters2 = copy.removeStaleFilters();
							return Status.OK_STATUS;
						}
					});

		}
	}; /*
		 * new Action("Remove Filters", Action.AS_PUSH_BUTTON) { {
		 * setImageDescriptor( PlatformUI.getWorkbench().getSharedImages()
		 * .getImageDescriptor (ISharedImages.IMG_TOOL_DELETE));
		 * setDisabledImageDescriptor (PlatformUI.getWorkbench()
		 * .getSharedImages() .getImageDescriptor(ISharedImages.
		 * IMG_TOOL_DELETE_DISABLED)); }
		 * 
		 * public void run() { copy.removeStaleFilters(); QueryEditorPart.this
		 * .firePropertyChange(PROP_DIRTY); } };
		 */

	public IAction getRemoveStaleFilters() {
		return removeStaleFilters;
	}

	public final boolean isDirty() {
		return !copy.equals(((QueryInput) getEditorInput()).getView());
	}

	public final boolean isSaveAsAllowed() {
		return true;
	}

	protected String currentSorting;

	public ColumnConfiguration getCurrentSorting() {
		String string = copy.getSettings().get(IRealmView.SORT_PROPERTY);
		for (ColumnConfiguration c : copy.getParent().getQueryProvider()
				.getAllowedColumns()) {
			if (c.getId().equals(string)) {
				return c;
			}
		}
		return null;
	}

	public void setCurrentSorting(Object data, boolean inverse) {
		ColumnConfiguration c = (ColumnConfiguration) data;
		currentSorting = c != null ? c.getId() : "";
		sortInverse = inverse;
		copy.put(IRealmView.SORT_ORDER, "" + sortInverse);
		copy.put(IRealmView.SORT_PROPERTY, currentSorting);
		firePropertyChange(PROP_DIRTY);
	}

	public IRealmView getView() {
		return query;
	}
}
